<html>
    <head>
        <meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
        <script>
            /*
            author: jiaochao
                    jiaochao@gmail.com

            1. plot back ground
            2. plot piece
            .. plot All pieces
            .. plot All (back ground, all pieces)
            .. remove pieces
            .. can place at one place
            .. restart
            .. replay
            */

            // get position of object
            function getPos(obj) {
                var curleft = curtop = 0;
                if (obj && obj.offsetParent) {
                    do {
                        curleft += obj.offsetLeft;
                        curtop += obj.offsetTop;
                    } while (obj = obj.offsetParent);
                }
                return {'x':curleft, 'y':curtop};
            }

            function RepEnv()
            {
                var data = [];
                return {'data':data,'current':0,'inReplay':false};
            }


            // env and consts
            function SimEnv()
            {
                // key state
                var black = 1;
                var white = 2;
                var empty = 0;
                var invalid = -1;

                // data info
                var size = 19;
                var next = black; // next side
                // create connection
                var outter = {'v':invalid,'x':-1,'y':-1,'top':null,'bot':null,'left':null,'right':null};
                outter.top = outter;
                outter.bot = outter;
                outter.left = outter;
                outter.right = outter;

                var current = outter;
                var conn = new Array();
                for(var i=0;i<size;++i)
                {
                    conn[i] = new Array();
                    for(var j=0;j<size;++j)
                    {
                        conn[i][j] = {'v':empty,'x':i,'y':j,'top':null,'bot':null,'left':null,'right':null};
                    }
                }
                // init connection
                for(var i=0;i<size;++i)
                {
                    for(var j=0;j<size;++j)
                    {
                        if(i == 0)
                        {
                            left = outter;
                            right = conn[i+1][j];
                        }
                        else if(i==size-1)
                        {
                            right = outter;
                            left = conn[i-1][j];
                        }
                        else
                        {
                            left = conn[i-1][j];
                            right = conn[i+1][j];
                        }
                        if(j == 0)
                        {
                            top1 = outter;
                            bot = conn[i][j+1];
                        }
                        else if(j==size-1)
                        {
                            bot = outter;
                            top1 = conn[i][j-1];
                        }
                        else
                        {
                            top1 = conn[i][j-1];
                            bot = conn[i][j+1];
                        }
                        conn[i][j].top = top1;
                        conn[i][j].bot = bot;
                        conn[i][j].left = left;
                        conn[i][j].right = right;
                    }
                }
                var ret = {'empty':empty,'black':black,'white':white,'size':size,'current':current,'next':next,'conn':conn};
                return ret;
            }

            // plot related env and consts
            function PlotEnv()
            {
                var cellSize = 26;
                var pieceSizeRatio = 0.8;
                var lineWidth= 2;
                var lineColor = 'rgb(200,200,200)';
                var bgColor = 'rgb(250,250,200)';
                var whiteColor = 'rgb(255,255,255)';
                var blackColor = 'rgb(0,0,0)';
                var simEnv = new SimEnv;
                var ctx = document.createElement("canvas").getContext('2d');
                var repEnv = new RepEnv();
                var actEnv = {'mouseMove':false,'mouseDown':false,'offset':null};
                var ret = {'cellSize':cellSize,'pieceSizeRatio':pieceSizeRatio,'lineWidth':lineWidth,'lineColor':lineColor,'whiteColor':whiteColor,'blackColor':blackColor,'bgColor':bgColor,'simEnv':simEnv,'repEnv':repEnv,'ctx':ctx,'actEnv':actEnv};
                return ret;
            }

            // setup the canvas size and position 
            function plotSetup(plotEnv)
            {
                var width = plotEnv.cellSize * (plotEnv.simEnv.size+1);
                var height = width;
                var ctx = plotEnv.ctx;
                ctx.canvas.width = width;
                ctx.canvas.height = height;
            }

            // plot the back ground and piece
            function plotBg(plotEnv)
            {
                var ctx = plotEnv.ctx;
                ctx.save();
                // bg
                ctx.fillStyle = plotEnv.bgColor;
                ctx.fillRect(0,0,ctx.canvas.width,ctx.canvas.height);

                // lines
                var size = plotEnv.simEnv.size;
                var cellSize = plotEnv.cellSize;
                ctx.strokeStyle = plotEnv.lineColor;
                ctx.lineWidth = plotEnv.lineWidth;
                for(var i=0;i<size;++i)
                {
                    var x = (i+1)*cellSize;
                    ctx.beginPath();
                    ctx.moveTo(x,cellSize);
                    ctx.lineTo(x,cellSize*size)
                    ctx.stroke();
                }
                for(var i=0;i<size;++i)
                {
                    var y = (i+1)*cellSize;
                    ctx.beginPath();
                    ctx.moveTo(cellSize,y);
                    ctx.lineTo(cellSize*size,y)
                    ctx.stroke();
                }
                ctx.restore();
            }

            // plot all back ground and all piece
            function plotAll(plotEnv)
            {
                plotBg(plotEnv);
                plotAllPiece(plotEnv);
            }

            // plot all piece
            function plotAllPiece(plotEnv)
            {
                conn = plotEnv.simEnv.conn;
                size = plotEnv.simEnv.size;
                for(var i=0;i<size;++i)
                {
                    for(var j=0;j<size;++j)
                    {
                        plotOnePiece(plotEnv,conn[i][j]);
                    }
                }
            }
            // plot one piece
            function plotOnePiece(plotEnv,connPos)
            {
                var nx = connPos.x;
                var ny = connPos.y;
                var val = connPos.v;
                var size = plotEnv.simEnv.size;
                if((val != plotEnv.simEnv.black) && (val != plotEnv.simEnv.white))
                {
                    return;
                }

                if(nx<0 || nx>=size || ny<0 || ny>=size)
                {
                    return;
                }
                var cellSize = plotEnv.cellSize;
                var pieceSize = cellSize * plotEnv.pieceSizeRatio;
                var x = (nx+1)*cellSize;
                var y = (ny+1)*cellSize;
                var ctx = plotEnv.ctx;
                var pieceColor;
                if(val == plotEnv.simEnv.black)
                {
                    pieceColor = plotEnv.blackColor;
                }
                else if(val == plotEnv.simEnv.white)
                {
                    pieceColor = plotEnv.whiteColor;
                }
                else
                {
                    // no need to plot
                    return ;
                }
                ctx.save();
                ctx.beginPath();
                ctx.fillStyle = pieceColor;
                ctx.arc(x,y,pieceSize/2,0,2*Math.PI);
                ctx.fill();
                ctx.stroke();
                ctx.restore();
            }

            // calculate what was the next
            function nextEng(simEnv)
            {
                var conn = simEnv.conn;
                var black = simEnv.black;
                var white = simEnv.white;
                var empty = simEnv.empty;
                var next = simEnv.next;

            }
            function findDeath(simEnv,connPos)
            {
            // if there is no empty around connPos, only invalid and opposite
                size = simEnv.size;
                flag = new Array();
                for(var i=0;i<size;++i)
                {
                    flag[i] = new Array();
                    for(var j=0;j<size;++j)
                    {
                        flag[i][j] = 0;
                    }
                }
                ret = [];
                stack = [];
                stack.push(connPos);
                while(stack.length > 0)
                {
                    connPos = stack.pop();
                    flag[connPos.x][connPos.y] = 1;
                    ret.push(connPos);
                    if(connPos.top.v == connPos.v)
                    {
                        if(flag[connPos.top.x][connPos.top.y] == 0)
                            stack.push(connPos.top);
                    }
                    if(connPos.bot.v == connPos.v)
                    {
                        if(flag[connPos.bot.x][connPos.bot.y] == 0)
                            stack.push(connPos.bot);
                    }
                    if(connPos.left.v == connPos.v)
                    {
                        if(flag[connPos.left.x][connPos.left.y] == 0)
                            stack.push(connPos.left);
                    }
                    if(connPos.right.v == connPos.v)
                    {
                        if(flag[connPos.right.x][connPos.right.y] == 0)
                            stack.push(connPos.right);
                    }
                } 
                for(var i=0;i<ret.length;++i)
                {
                    if((ret[i].top.v == simEnv.empty) || (ret[i].bot.v == simEnv.empty) || (ret[i].left.v == simEnv.empty) || (ret[i].right.v == simEnv.empty))
                    {
                        ret = null;
                        return [];
                    }
                }
                return ret;
            }
            
            // get removes
            function getRemoves(simEnv,current)
            {
                var opp;
                // current must not be invalid
                if(current.v == simEnv.black)
                {
                    opp = simEnv.white;
                }
                else if(current.v == simEnv.white)
                {
                    opp = simEnv.black;
                }
                else
                {
                    alert("error occur");
                }

                // check top,bot,left,right
                var death= [];
                if(current.top.v == opp)
                {
                    death = death.concat(findDeath(simEnv,current.top));
                }
                if(current.bot.v == opp)
                {
                    death = death.concat(findDeath(simEnv,current.bot));
                }
                if(current.left.v == opp)
                {
                    death = death.concat(findDeath(simEnv,current.left));
                }
                if(current.right.v == opp)
                {
                    death = death.concat(findDeath(simEnv,current.right));
                }
                return death;
            }
            // remove items
            function updateRemove(simEnv)
            {
                var death = getRemoves(simEnv,simEnv.current);
                for(var i=0;i<death.length;++i)
                {
                    death[i].v = simEnv.empty;
                }
            }

            function canPlace(simEnv,connPos)
            {
                if(connPos.v != simEnv.empty)
                {
                    return false;
                }

                connPos.v = simEnv.next;
                var ret = findDeath(simEnv,connPos);
                if(ret.length != 0)
                {
                    ret = getRemoves(simEnv,connPos);
                    if(ret.length == 0)
                    {
                        connPos.v = simEnv.empty;
                        return false;
                    }
                }
                connPos.v = simEnv.empty;
                return true;
            }

            function printMsg(msg)
            {
                var str = msg + "<br>" + document.getElementById('text').innerHTML ;
                document.getElementById('text').innerHTML = str;
            }
            function cleanMsg()
            {
                document.getElementById('text').innerHTML = 'Cleaned!';
            }

            function placePiece(plotEnv,nx,ny,needSave)
            {
                var size = plotEnv.simEnv.size;
                if( (nx<0) || (ny<0) || (nx>=size) || (ny>=size))
                {
                    return;
                }
                if(!canPlace(plotEnv.simEnv,plotEnv.simEnv.conn[nx][ny]))
                {
                    printMsg("You CANNOT place at that place!");
                    return;
                }
                var msg = "place at: (" + (nx+1) + "," + (ny+1) + ")";
                printMsg(msg);

                if(plotEnv.simEnv.conn[nx][ny].v == plotEnv.simEnv.empty)
                {
                    // prepare for replay
                    if(needSave)
                    {
                        plotEnv.repEnv.data.push({'x':nx,'y':ny});
                    }
                    plotEnv.simEnv.current = plotEnv.simEnv.conn[nx][ny];
                    plotEnv.simEnv.conn[nx][ny].v = plotEnv.simEnv.next;
                    plotOnePiece(plotEnv,plotEnv.simEnv.conn[nx][ny]);
                    if(plotEnv.simEnv.next == plotEnv.simEnv.white)
                    {
                        plotEnv.simEnv.next = plotEnv.simEnv.black;
                    }
                    else
                    {
                        plotEnv.simEnv.next = plotEnv.simEnv.white;
                    }

                    // update remove
                    updateRemove(plotEnv.simEnv);
                    plotAll(plotEnv);
                }
            }

            // human action
            function mDown(ev,plotEnv)
            {
                // plotting
                var offset = getPos(plotEnv.ctx.canvas);
                var x = ev.pageX-offset.x;
                var y = ev.pageY-offset.y;
                offset = xy2n(plotEnv,x,y);
                if(offset.x <0 || offset.y<0)
                {
                    return;
                }
                if(plotEnv.repEnv.inReplay)
                {
                    printMsg("Replay Mode, please restart!");
                    return;
                }
                placePiece(plotEnv,offset.x,offset.y,true);
            }

            // touch
            function touchDown(e)
            {
                plotEnv.actEnv.mouseMove = false;
                var can = plotEnv.ctx.canvas;
                var offset = getPos(plotEnv.ctx.canvas);
                var x = e.targetTouches[0].pageX - offset.x;//can.offsetLeft;
                var y = e.targetTouches[0].pageY - offset.y;//can.offsetTop;
                offset = xy2n(plotEnv,x,y);
                plotEnv.actEnv.offset = offset;
            }
            function touchUp(e)
            {
                if(plotEnv.actEnv.mouseMove)
                {
                    return;
                }
                var offset = plotEnv.actEnv.offset;
                if(offset.x < 0 || offset.y<0)
                {
                    return;
                }
                if(plotEnv.repEnv.inReplay)
                {
                    printMsg("Replay Mode, please restart!");
                    return;
                }
                placePiece(plotEnv,offset.x,offset.y,true);
            }
            function touchMove(e)
            {
                plotEnv.actEnv.mouseMove = true;
            }

            // convert x,y coordinate to the index
            function xy2n(plotEnv,x,y)
            {
                var cellSize = plotEnv.cellSize;
                var size = plotEnv.simEnv.size;
                var nx = -1;
                var ny = -1;
                x = x-0.5*cellSize;
                y = y-0.5*cellSize;
                if(x>0 && x<size*cellSize && y>0 && y<size*cellSize)
                {
                    nx = Math.floor(x/cellSize);
                    ny = Math.floor(y/cellSize);
                }

                return {'x':nx,'y':ny};
            }

            function initSimEnv(simEnv)
            {
                simEnv.next = simEnv.black;
                var size = simEnv.size;
                var conn = simEnv.conn;
                var empty = simEnv.empty;
                for(var i=0;i<size;++i)
                {
                    for(var j=0;j<size;++j)
                    {
                        conn[i][j].v = empty;
                    }
                }
            }
            // restart 
            function restart(plotEnv)
            {
                printMsg("Restart Now!");
                // init simEnv
                initSimEnv(plotEnv.simEnv);

                // init repEnv
                plotEnv.repEnv.data = [];
                plotEnv.repEnv.inReplay = false;
                plotBg(plotEnv);
            }

            // replay
            function replay(plotEnv)
            {
                plotEnv.repEnv.inReplay = true;
                var str = "Replay step: " + plotEnv.repEnv.current + "/" + plotEnv.repEnv.data.length;
                printMsg(str);
                if(plotEnv.repEnv.data.length == 0)
                {
                    printMsg("Nothing to replay!");
                    return;
                }
                if(plotEnv.repEnv.current>=plotEnv.repEnv.data.length)
                {
                    printMsg("Get the end of replay! will restart replay!");
                    plotEnv.repEnv.current = 0;
                }
                if(plotEnv.repEnv.current == 0)
                {
                    initSimEnv(plotEnv.simEnv);
                    plotBg(plotEnv);
                }
                var offset = plotEnv.repEnv.data[plotEnv.repEnv.current];
                plotEnv.repEnv.current += 1;
                placePiece(plotEnv,offset.x,offset.y,false);
            }

            // main code
            var plotEnv = new PlotEnv();
            plotSetup(plotEnv);
            plotBg(plotEnv);
        </script>
    </head>
    <body onMouseDown="mDown(event,plotEnv);">
        <div>WeiQi by jiaochao</div>
        <table>
        <tr>
            <td><div id='qipan'></div></td>
            <td>
                <button id="restart" style="display:block;" onclick="restart(plotEnv);">Restart</button>
                <button id="replay" style="display:block;" onclick="replay(plotEnv);">Replay</button>
                <button id="clean" style="display:block;" onclick="cleanMsg();">Clean Message</button>
                <div id="text" style="height:200px;width:200px;overflow-y:scroll;">Wei Qi</div> 
            </td>
        </tr>
        </table>

        <script>
            document.getElementById('qipan').style.width = plotEnv.ctx.canvas.width;
            document.getElementById('qipan').style.height = plotEnv.ctx.canvas.height;
            document.getElementById('qipan').appendChild(plotEnv.ctx.canvas);

            var can = plotEnv.ctx.canvas;
            can.addEventListener("touchstart", touchDown, false);
            can.addEventListener("touchmove", touchMove, true);
            can.addEventListener("touchend", touchUp, false);
        </script>
    </body>
</html>
